home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C++ / Applications / PICSee Dust 1.01 / Quaternary Source / SmartDragWindow.c < prev    next >
Text File  |  1995-11-22  |  24KB  |  779 lines

  1. /*
  2.     SmartDragWindow.c
  3.  
  4.     Written by Hiep Dam
  5.     From The Witches' Brew
  6.     Date: November 16, 1995
  7.     Internet: starlabs@aol.com
  8.  
  9.     ---------------------------------------------------------------------
  10.     
  11.     This is in the public domain. Insert it into all of your applications!
  12.     (There's no reason not to!) <grin>
  13.     
  14.     This routine does the right thing for normal & tricky situations:
  15.         * ...handling multiple monitor situations
  16.           (thanks to GetDominantDevice())
  17.         * ...not drawing over other windows if they are on top
  18.           of the window that is dragged (a subtle fact missed until
  19.           I noticed how the Finder implemented command-dragging).
  20.         * ...hiding the drag outline if the mouse moves outside of
  21.           the limit rect - just like DragWindow.
  22.  
  23.  
  24.     Note: Because this routine fiddles with the Window Manager port,
  25.     compatibility with future versions of the system software may be
  26.     compromised. But this looks to be waaaaay in the future...
  27.  
  28.     The code is "somewhat" Copland-aware (uses the STRICT_WINDOWS accessors).
  29.     
  30.     Feel free to make changes/improvements to the code and distribute
  31.     it as you wish. Just drop me a copy, OK?
  32.  
  33.     ---------------------------------------------------------------------
  34.  
  35.     Portions written by Norman Basham, and a few ideas here and there
  36.     taken from C.K. Haun.
  37.     
  38.     Version History:
  39.         1.0    11/16/95    Wrote it. Initially much easier than I thought.
  40.                         Then slightly harder when trying to handle the
  41.                         tricky situations above. But still not difficult
  42.                         at all...
  43.         1.1 11/18/95    Added support for snap procs, and thus support
  44.                         for grid snapping and window snapping as well.
  45.         1.0.1 11/18/95    Noticed slight discrepancy when dragging near
  46.                         menubar. Added code to exclude menubar region
  47.                         as if it was an overlapping window.
  48. */
  49.  
  50.  
  51. #include "SmartDragWindow.h"
  52. #ifndef __LOWMEM__
  53. #include <LowMem.h>
  54. #endif
  55.  
  56. // ---------------------------------------------------------------------------
  57.  
  58. static Boolean DoWindowDrag(
  59.     WindowPtr    dragWindow,
  60.     Point        startPoint,
  61.     const Rect    *limitRect,
  62.     short        snapToDistance,
  63.     Rect        *dragResultRect,
  64.     SnapCallback snapProc);
  65.  
  66. static void AdjustWindowForStructure(WindowRef theWindow, Rect *adjustRect);
  67.  
  68. static void SnapBottomToBottom(Rect *snapRect, const Rect *magnetRect, short snapToDistance);
  69. static void SnapTopToTop(Rect *snapRect, const Rect *magnetRect, short snapToDistance);
  70. static void SnapRightToRight(Rect *snapRect, const Rect *magnetRect, short snapToDistance);
  71. static void SnapLeftToLeft(Rect *snapRect, const Rect *magnetRect, short snapToDistance);
  72.  
  73. static Boolean WithinRange(short theValue, short rangeValue, short range);
  74. static void MoveRectTo(Rect *theRect, short h, short v);
  75.  
  76. static GDHandle GetDominantDevice (Rect *r);
  77. static Boolean IsActiveScreenDevice();
  78. static long GetRectArea(Rect r);
  79.  
  80. // ---------------------------------------------------------------------------
  81.  
  82. #pragma mark === Hiep Dam ===
  83.  
  84.  
  85. enum {
  86.     kSnapToMargin = 10
  87. };
  88.  
  89. #define kExtremeNeg        -32768
  90. #define kExtremePos        (32767 - 1) // required to address an old region bug,
  91.                                     //see develop 20 Q&As
  92.  
  93. #define HOTKEY_DISABLE    IsControlKeyDown()
  94.  
  95. // ---------------------------------------------------------------------------
  96.  
  97. void SmartDragWindow(
  98.     WindowRef windowToDrag,
  99.     Point startPoint,
  100.     const Rect *limitRect,
  101.     short snapToDistance) {
  102.     
  103.     SuperSmartDragWindow(windowToDrag, startPoint, limitRect, snapToDistance, MonitorSnapProc);
  104. } // END SmartDragWindow
  105.  
  106. // ---------------------------------------------------------------------------
  107.  
  108. void SuperSmartDragWindow(
  109.     WindowRef windowToDrag,
  110.     Point startPoint,
  111.     const Rect *limitRect,
  112.     short snapToDistance,
  113.     SnapCallback snapProc) {
  114.  
  115.     GrafPtr        wMgrPort;
  116.     RgnHandle    wMgrSaveRgn;
  117.     RgnHandle    clipRgn;
  118.     RgnHandle    strucRgn;
  119.     PenState    wMgrPenState;
  120.     Rect        dragResult;
  121.     Rect        menuBarRect;
  122.     Boolean        selectWindow;
  123.     Boolean        dragOK;
  124.     WindowPtr    aWindow;
  125.     GrafPtr        savePort;
  126.     Rect        wideOpen;
  127.  
  128.     if (HOTKEY_DISABLE)
  129.         snapProc = NULL;
  130.  
  131.     // Is this a normal drag or a "cmd-key" drag?
  132.     selectWindow = true;
  133.     if (windowToDrag != FrontWindow() && IsCmdKeyDown())
  134.         selectWindow = false;
  135.  
  136.     GetPort(&savePort);
  137.     GetWMgrPort(&wMgrPort);
  138.     
  139.     // Set to window manager port & get its settings (pen, clipping region)
  140.     SetPort(wMgrPort);
  141.     GetPenState(&wMgrPenState);
  142.     wMgrSaveRgn = NewRgn();
  143.     GetClip(wMgrSaveRgn);
  144.  
  145.     // Enlarge clipping area so we can drag all over the place...
  146.     SetRect(&wideOpen, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
  147.     ClipRect(&wideOpen);
  148.     clipRgn = NewRgn();
  149.     GetClip(clipRgn);
  150.     
  151.     strucRgn = NewRgn();
  152.     // Account for any windows which may be on top of ours. If there
  153.     // are any, subtract their structure regions from the clipping
  154.     // region so we don't draw into them (just like what DragWindow() does).
  155.     if (windowToDrag != FrontWindow()) {
  156.         // Get first window
  157.         aWindow = FrontWindow();
  158.  
  159.         while (aWindow != windowToDrag && aWindow != NULL) {
  160.             if (IsWindowVisible(aWindow)) {
  161.                 GetWindowStructureRgn(aWindow, strucRgn);
  162.                 DiffRgn(clipRgn, strucRgn, clipRgn);
  163.             }
  164.             aWindow = GetNextWindow(aWindow);
  165.         }
  166.     }
  167.  
  168.     // Remember to exclude menu bar as well. In this case we treat
  169.     // it like one of the windows above - we subtract it's area
  170.     // from the total draggable clipping region.
  171.     // This part unfortunately relies on a low-memory global...
  172.     menuBarRect = (**GetMainDevice()).gdRect;
  173.     menuBarRect.bottom = menuBarRect.top + LMGetMBarHeight();
  174.     RectRgn(strucRgn, &menuBarRect);
  175.     DiffRgn(clipRgn, strucRgn, clipRgn);
  176.     DisposeRgn(strucRgn);
  177.  
  178.     // Set our "adjusted" clipping region
  179.     SetClip(clipRgn);
  180.  
  181.     // Illegal snap-to distance?
  182.     if (snapToDistance < 0)
  183.         snapToDistance = kSnapToMargin;
  184.  
  185.     // Do the actual dirty work
  186.     dragOK = DoWindowDrag(
  187.         windowToDrag,
  188.         startPoint,
  189.         limitRect,
  190.         snapToDistance,
  191.         &dragResult,
  192.         snapProc);
  193.     
  194.     // Restore window manager port
  195.     SetClip(wMgrSaveRgn);
  196.     SetPenState(&wMgrPenState);
  197.     DisposeRgn(wMgrSaveRgn);
  198.     DisposeRgn(clipRgn);
  199.  
  200.     if (dragOK) {
  201.         // Adjust dragResult for structure region of window (i.e. drag bar)
  202.         AdjustWindowForStructure(windowToDrag, &dragResult);
  203.         MoveWindow(windowToDrag, dragResult.left, dragResult.top, selectWindow);
  204.     }
  205.  
  206.     SetPort(savePort);
  207. } // END SmartDragWindow
  208.  
  209. // ---------------------------------------------------------------------------
  210.  
  211. /*
  212.     DoWindowDrag().
  213.     The real meat 'n potatoes of the outfit.
  214.     
  215.     Returns true if mouseloc was inside limitRect when mouse was released,
  216.     else false.
  217.     If true, argument <dragResultRect> will contain the new dragged rect
  218.     (note: the rect is the size of the window's structure region and
  219.     needs to be adjusted before calling MoveWindow())
  220. */
  221.  
  222. Boolean DoWindowDrag(
  223.     WindowPtr    dragWindow,
  224.     Point        startPoint,
  225.     const Rect    *limitRect,
  226.     short        snapToDistance,
  227.     Rect        *dragResultRect,
  228.     SnapCallback snapProc) {
  229.  
  230.     Rect        dragRect;
  231.     Rect        oldRect;
  232.     Rect        confineRect;
  233.     Rect        grayRgnRect;
  234.     RgnHandle    strucRgn;
  235.     Point        mouseLoc;
  236.     short        offsetH,
  237.                 offsetV;
  238.     short        windowWidth,
  239.                 windowHeight;
  240.     Boolean        insideConfineRect;
  241.     char        i;
  242.     Pattern        grayPat;
  243.     Pattern        blackPat;
  244.  
  245.     /*
  246.         Why can't we auto-initialize the patterns grayPat & blackPat,
  247.         as in "grayPat = { 0xAA, 0x55, 0xAA, 0x55, etc... }"?
  248.  
  249.         Well, looking at the definition of a Pattern, it's a string
  250.         of 8 chars! So the above is a string initialization, which in
  251.         turn makes the initialization string 0xAA, 0x55 a global
  252.         string. This is NOT what is intended! Doing this will cause
  253.         the compiler to allocate at least 16 bytes of global data.
  254.     */
  255.     for (i = 0; i < 8; i++) {
  256.         grayPat.pat[i] = 0xAA;
  257.         blackPat.pat[i] = 0xAA;
  258.     }
  259.     for (i = 1; i < 8; i += 2)
  260.         grayPat.pat[i] = 0x55;
  261.  
  262.     // Determine limit rect (& be smart about it!)
  263.     grayRgnRect = (**GetGrayRgn()).rgnBBox;
  264.     if (limitRect == NULL || EmptyRect(limitRect)) {
  265.         // Don't use screenBits.bounds, please!
  266.         confineRect = grayRgnRect;
  267.         InsetRect(&confineRect, 4, 4);
  268.     }
  269.     else {
  270.         // If users passed the grayRgn or screenBits.bounds, adjust
  271.         // the limit rect for them, just like DragWindow() does.
  272.         // We shouldn't use globals, so assuming that screenBits.bounds
  273.         // is the same as (**GetMainDevice).gdRect...
  274.         if (EqualRect(limitRect, &grayRgnRect) ||
  275.             EqualRect(limitRect, &(**GetMainDevice()).gdRect)) {
  276.             confineRect = grayRgnRect;
  277.             InsetRect(&confineRect, 4, 4);
  278.         }
  279.         else {
  280.             // Passed a non-standard limit rect. Use it instead.
  281.             confineRect = *limitRect;
  282.         }
  283.     }
  284.     insideConfineRect = true;
  285.     
  286.     // Get window height, width
  287.     strucRgn = NewRgn();
  288.     GetWindowStructureRgn(dragWindow, strucRgn);
  289.     windowWidth = dragRect.right - dragRect.left;
  290.     windowHeight = dragRect.bottom - dragRect.top;
  291.     dragRect = (**strucRgn).rgnBBox;
  292.  
  293.     // Since the mouse probably isn't at the topleft of the window,
  294.     // we have to make adjustments
  295.     offsetH = startPoint.h - dragRect.left;
  296.     offsetV = startPoint.v - dragRect.top;
  297.  
  298.     // Now we can rubberband
  299.     PenMode(srcXor);
  300.     PenPat(&grayPat);
  301.     // Draw it for the first time
  302.     oldRect = dragRect;
  303.     FrameRect(&dragRect);
  304.  
  305.     while (StillDown()) {        
  306.         GetMouse(&mouseLoc);
  307.  
  308.         if (PtInRect(mouseLoc, &confineRect)) {
  309.             MoveRectTo(&dragRect, mouseLoc.h - offsetH, mouseLoc.v - offsetV);
  310.             
  311.             if (snapProc)
  312.                 snapProc(dragWindow, snapToDistance, &dragRect);
  313.  
  314.             // Draw only if mouse moved and it moved inside of limit rect
  315.             if (!insideConfineRect) {
  316.                 // Mouse was outside of limit rect and now it
  317.                 // has moved back inside.
  318.                 FrameRect(&dragRect);
  319.                 oldRect = dragRect;
  320.             }
  321.             else if (!EqualRect(&oldRect, &dragRect)) {
  322.                 FrameRect(&dragRect);
  323.                 FrameRect(&oldRect);
  324.                 oldRect = dragRect;
  325.             }
  326.  
  327.             insideConfineRect = true;
  328.         }
  329.         else {
  330.             if (insideConfineRect) {
  331.                 // Mouse was inside limit rect and now has moved
  332.                 // outside of it.
  333.  
  334.                 // Erase drag outline
  335.                 FrameRect(&oldRect);
  336.                 insideConfineRect = false;
  337.             }
  338.         }
  339.     }
  340.     
  341.     // Erase the last drag outline (if it hasn't been
  342.     // already erased by the mouse being outside of confineRect)
  343.     if (insideConfineRect)
  344.         FrameRect(&dragRect);
  345.  
  346.     DisposeRgn(strucRgn);
  347.  
  348.     // Restore drawing modes
  349.     PenMode(srcCopy);
  350.     PenPat(&blackPat);
  351.     
  352.     if (insideConfineRect)
  353.         *dragResultRect = dragRect;
  354.  
  355.     return(insideConfineRect);
  356. } // END DoWindowDrag
  357.  
  358. // ---------------------------------------------------------------------------
  359.  
  360. /*
  361.     Adjust the drag rect by accounting for the window's drag bar.
  362.     DoWindowDrag() returns the dragged rect - but this rect is the
  363.     total structure rect of the window. MoveWindow(), on the other
  364.     hand, expects the rect to be in terms of the content region.
  365.     So we have to adjust it...
  366. */
  367.  
  368. void AdjustWindowForStructure(WindowRef theWindow, Rect *adjustRect) {
  369.     RgnHandle strucRgn, contRgn;
  370.     
  371.     strucRgn = NewRgn();
  372.     contRgn = NewRgn();
  373.     
  374.     GetWindowStructureRgn(theWindow, strucRgn);
  375.     GetWindowContentRgn(theWindow, contRgn);
  376.     
  377.     // We only have to worry about the left and top sides
  378.     // of the rect, since MoveWindow takes only h and v arguments.
  379.     adjustRect->top += (**contRgn).rgnBBox.top - (**strucRgn).rgnBBox.top;
  380.     adjustRect->left += (**contRgn).rgnBBox.left - (**strucRgn).rgnBBox.left;
  381.     
  382.     // Here is a tricky situation. If the window is "rolled up", a la
  383.     // Aaron, we get errorneous contRgn areas. The top
  384.     // of the contRgn is one less than it is if it wasn't rolled up.
  385.     // So we have to adjust for this.
  386.     // WindowShade does use the correct values.
  387.     /*
  388.     if ((**contRgn).rgnBBox.top == (**contRgn).rgnBBox.bottom) {
  389.         adjustRect->top++;
  390.     }
  391.     */
  392.  
  393.     DisposeRgn(strucRgn);
  394.     DisposeRgn(contRgn);
  395. } // END AdjustWindowForStructure
  396.  
  397. // ---------------------------------------------------------------------------
  398.  
  399. /*
  400.     MonitorSnapProc.
  401.     Snaps the rect to the edges of the monitor. Multiple-monitor savvy.
  402. */
  403.  
  404. void MonitorSnapProc(WindowPtr windowToDrag, short snapToDistance, Rect *snapRect) {
  405.     GDHandle    theMonitor;
  406.     Rect        monitorRect;
  407.  
  408.     // This is where we adjust the drag rect to "snap to" the
  409.     // edges of the monitors.
  410.     theMonitor = GetDominantDevice(snapRect);
  411.     monitorRect = (**theMonitor).gdRect;
  412.     if (theMonitor == GetMainDevice()) {
  413.         // If this is the main monitor, adjust "snap to" rect
  414.         // to account for menu bar.
  415.         monitorRect.top += LMGetMBarHeight();
  416.     }
  417.  
  418.     // Is the rect close enought to left or right edges
  419.     // of the magnetRect?
  420.     SnapRightToRight(snapRect, &monitorRect, snapToDistance);
  421.     SnapLeftToLeft(snapRect, &monitorRect, snapToDistance);
  422.  
  423.     // How about the top or bottom edges?
  424.     SnapBottomToBottom(snapRect, &monitorRect, snapToDistance);
  425.     SnapTopToTop(snapRect, &monitorRect, snapToDistance);
  426. } // END MonitorSnapProc
  427.  
  428. // ---------------------------------------------------------------------------
  429.  
  430. /*
  431.     WindowSnapProc.
  432.     Snap to edges of other windows.
  433. */
  434.  
  435. enum {
  436.     kMaxWindowsSnap = 10
  437. };
  438.  
  439. void WindowSnapProc(WindowPtr windowToDrag, short snapToDistance, Rect *snapRect) {
  440.     Rect strucRgnRect;
  441.     short snapRectWidth, snapRectHeight;
  442.     short i, numWindows;
  443.     WindowRef theWindow;
  444.     WindowRef windowList[kMaxWindowsSnap];
  445.     RgnHandle strucRgn;
  446.     
  447.     theWindow = FrontWindow();
  448.     if (theWindow == NULL) return;
  449.     
  450.     snapRectWidth = snapRect->right - snapRect->left;
  451.     snapRectHeight = snapRect->bottom - snapRect->top;
  452.  
  453.     // Make a list of windows which meet our criteria
  454.     // of "snappable" windows
  455.     i = numWindows = 0;
  456.     while ((theWindow != NULL) && (i < kMaxWindowsSnap)) {
  457.         if (IsWindowVisible(theWindow) && (theWindow != windowToDrag)) {
  458.             windowList[i++] = theWindow;
  459.             numWindows++;
  460.         }
  461.         theWindow = GetNextWindow(theWindow);
  462.     }
  463.  
  464.     strucRgn = NewRgn();
  465.  
  466.     // We're going to give the topmost window the highest priority
  467.     // when determining which window to snap to. To do this, we
  468.     // step through the window list backwards (since the topmost
  469.     // window is the first entry in the window list).
  470.     for (i = numWindows-1; i >= 0; i--) {
  471.         GetWindowStructureRgn(windowList[i], strucRgn);
  472.         strucRgnRect = (**strucRgn).rgnBBox;
  473.         
  474.         /*
  475.             Below is a tortuous road of if-then-checks. sigh.
  476.  
  477.             The problem here is that we have to do eight checks:
  478.             the left of the snap window to the left of the magnet window,
  479.             the left of the snap window to the right of the magnet window,
  480.             the top of the snap window to the top of the magnet window,
  481.             the top of the snap window to the bottom of the magnet window,
  482.             and so on for all 4 sides x 2...
  483.         */
  484.  
  485.         // Check left to left snapping
  486.         if (WithinRange(snapRect->left, strucRgnRect.left, snapToDistance)) {
  487.             if ((snapRect->top > strucRgnRect.bottom) ||
  488.                 (snapRect->bottom < strucRgnRect.top)) {
  489.                 // Do nothing
  490.             }
  491.             else {
  492.                 snapRect->left = strucRgnRect.left;
  493.                 snapRect->right = snapRect->left + snapRectWidth;
  494.             }
  495.             // Top gets preference, so we check that last
  496.             SnapBottomToBottom(snapRect, &strucRgnRect, snapToDistance);
  497.             SnapTopToTop(snapRect, &strucRgnRect, snapToDistance);
  498.         }
  499.         // Check right to right snapping
  500.         else if (WithinRange(snapRect->right, strucRgnRect.right, snapToDistance)) {
  501.             if ((snapRect->top > strucRgnRect.bottom) ||
  502.                 (snapRect->bottom < strucRgnRect.top)) {
  503.                 // Do nothing
  504.             }
  505.             else {
  506.                 snapRect->right = strucRgnRect.right;
  507.                 snapRect->left = snapRect->right - snapRectWidth;
  508.             }
  509.             // Top gets preference, so we check that last
  510.             SnapBottomToBottom(snapRect, &strucRgnRect, snapToDistance);
  511.             SnapTopToTop(snapRect, &strucRgnRect, snapToDistance);
  512.         }
  513.  
  514.  
  515.         // Check top to top snapping
  516.         if (WithinRange(snapRect->top, strucRgnRect.top, snapToDistance)) {
  517.             if ((snapRect->left > strucRgnRect.right) ||
  518.                 (snapRect->right < strucRgnRect.left)) {
  519.                 // Do nothing
  520.             }
  521.             else {
  522.                 snapRect->top = strucRgnRect.top;
  523.                 snapRect->bottom = snapRect->top + snapRectHeight;
  524.             }
  525.             // Left get preference, so check that last
  526.             SnapRightToRight(snapRect, &strucRgnRect, snapToDistance);
  527.             SnapLeftToLeft(snapRect, &strucRgnRect, snapToDistance);
  528.         }
  529.         // Check bottom to bottom snapping
  530.         else if (WithinRange(snapRect->bottom, strucRgnRect.bottom, snapToDistance)) {
  531.             if ((snapRect->left > strucRgnRect.right) ||
  532.                 (snapRect->right < strucRgnRect.left)) {
  533.                 // Do nothing
  534.             }
  535.             else {
  536.                 snapRect->bottom = strucRgnRect.bottom;
  537.                 snapRect->top = snapRect->bottom - snapRectHeight;
  538.             }
  539.             // Left get preference, so check that last
  540.             SnapRightToRight(snapRect, &strucRgnRect, snapToDistance);
  541.             SnapLeftToLeft(snapRect, &strucRgnRect, snapToDistance);
  542.         }
  543.  
  544.  
  545.         // Check left to right snapping
  546.         if (WithinRange(snapRect->left, strucRgnRect.right, snapToDistance)) {
  547.             if ((snapRect->top > strucRgnRect.bottom) ||
  548.                 (snapRect->bottom < strucRgnRect.top)) {
  549.                 // Do nothing
  550.             }
  551.             else {
  552.                 snapRect->left = strucRgnRect.right;
  553.                 snapRect->right = snapRect->left + snapRectWidth;
  554.                 
  555.             }
  556.             // Top gets preference, so we check that last
  557.             SnapBottomToBottom(snapRect, &strucRgnRect, snapToDistance);
  558.             SnapTopToTop(snapRect, &strucRgnRect, snapToDistance);
  559.         }
  560.         // Check right to left snapping
  561.         else if (WithinRange(snapRect->right, strucRgnRect.left, snapToDistance)) {
  562.             if ((snapRect->top > strucRgnRect.bottom) ||
  563.                 (snapRect->bottom < strucRgnRect.top)) {
  564.                 // Do nothing
  565.             }
  566.             else {
  567.                 snapRect->right = strucRgnRect.left;
  568.                 snapRect->left = snapRect->right - snapRectWidth;
  569.             }
  570.             // Top gets preference, so we check that last
  571.             SnapBottomToBottom(snapRect, &strucRgnRect, snapToDistance);
  572.             SnapTopToTop(snapRect, &strucRgnRect, snapToDistance);
  573.         }
  574.  
  575.  
  576.         // Check top to bottom snapping
  577.         if (WithinRange(snapRect->top, strucRgnRect.bottom, snapToDistance)) {
  578.             if ((snapRect->left > strucRgnRect.right) ||
  579.                 (snapRect->right < strucRgnRect.left)) {
  580.                 // Do nothing
  581.             }
  582.             else {
  583.                 snapRect->top = strucRgnRect.bottom;
  584.                 snapRect->bottom = snapRect->top + snapRectHeight;
  585.             }
  586.             // Left get preference, so check that last
  587.             SnapRightToRight(snapRect, &strucRgnRect, snapToDistance);
  588.             SnapLeftToLeft(snapRect, &strucRgnRect, snapToDistance);
  589.         }
  590.         // Check bottom to top snapping
  591.         else if (WithinRange(snapRect->bottom, strucRgnRect.top, snapToDistance)) {
  592.             if ((snapRect->left > strucRgnRect.right) ||
  593.                 (snapRect->right < strucRgnRect.left)) {
  594.                 // Do nothing
  595.             }
  596.             else {
  597.                 snapRect->bottom = strucRgnRect.top;
  598.                 snapRect->top = snapRect->bottom - snapRectHeight;
  599.             }
  600.             // Left get preference, so check that last
  601.             SnapRightToRight(snapRect, &strucRgnRect, snapToDistance);
  602.             SnapLeftToLeft(snapRect, &strucRgnRect, snapToDistance);
  603.         }
  604.     }
  605.     
  606.     DisposeRgn(strucRgn);
  607. } // END WindowSnapProc
  608.  
  609. // ---------------------------------------------------------------------------
  610.  
  611. /*
  612.     GridSnapProc.
  613.     Snap rect to the grid.
  614. */
  615.  
  616. void GridSnapProc(WindowPtr windowToDrag, short gridSize, Rect *snapRect) {
  617.     short leftMod, topMod;
  618.     short snapRectWidth, snapRectHeight;
  619.  
  620.     snapRectWidth = snapRect->right - snapRect->left;
  621.     snapRectHeight = snapRect->bottom - snapRect->top;
  622.     
  623.     leftMod = snapRect->left / gridSize;
  624.     snapRect->left = leftMod * gridSize;
  625.     topMod = snapRect->top / gridSize;
  626.     snapRect->top = topMod * gridSize;
  627.     snapRect->right = snapRect->left + snapRectWidth;
  628.     snapRect->bottom = snapRect->top + snapRectHeight;
  629. } // END GridSnapProc
  630.  
  631. // ---------------------------------------------------------------------------
  632.  
  633. void SnapBottomToBottom(Rect *snapRect, const Rect *magnetRect, short snapToDistance) {
  634.     short snapRectHeight;
  635.  
  636.     if (WithinRange(snapRect->bottom, magnetRect->bottom, snapToDistance)) {
  637.         snapRectHeight = snapRect->bottom - snapRect->top;
  638.         snapRect->bottom = magnetRect->bottom;
  639.         snapRect->top = snapRect->bottom - snapRectHeight;
  640.     }
  641. } // END SnapBottomToBottom
  642.  
  643. void SnapTopToTop(Rect *snapRect, const Rect *magnetRect, short snapToDistance) {
  644.     short snapRectHeight;
  645.  
  646.     if (WithinRange(snapRect->top, magnetRect->top, snapToDistance)) {
  647.         snapRectHeight = snapRect->bottom - snapRect->top;
  648.         snapRect->top = magnetRect->top;
  649.         snapRect->bottom = snapRect->top + snapRectHeight;
  650.     }
  651. } // END SnapTopToTop
  652.  
  653. void SnapRightToRight(Rect *snapRect, const Rect *magnetRect, short snapToDistance) {
  654.     short snapRectWidth;
  655.  
  656.     if (WithinRange(snapRect->right, magnetRect->right, snapToDistance)) {
  657.         snapRectWidth = snapRect->right - snapRect->left;
  658.         snapRect->right = magnetRect->right;
  659.         snapRect->left = snapRect->right - snapRectWidth;
  660.     }
  661. } // END SnapRightToRight
  662.  
  663. void SnapLeftToLeft(Rect *snapRect, const Rect *magnetRect, short snapToDistance) {
  664.     short snapRectWidth;
  665.  
  666.     if (WithinRange(snapRect->left, magnetRect->left, snapToDistance)) {
  667.         snapRectWidth = snapRect->right - snapRect->left;
  668.         snapRect->left = magnetRect->left;
  669.         snapRect->right = snapRect->left + snapRectWidth;
  670.     }
  671. } // END SnapLeftToLeft
  672.  
  673. // ---------------------------------------------------------------------------
  674.  
  675. Boolean WithinRange(short theValue, short rangeValue, short range) {
  676.     if (theValue < rangeValue + range &&
  677.         theValue > rangeValue - range)
  678.         return(true);
  679.     else
  680.         return(false);
  681. } // END WithinRange
  682.  
  683. // ---------------------------------------------------------------------------
  684.  
  685. void MoveRectTo(Rect *theRect, short h, short v) {
  686.     short width = theRect->right - theRect->left;
  687.     short height = theRect->bottom - theRect->top;
  688.     theRect->left = h;
  689.     theRect->top = v;
  690.     theRect->right = theRect->left + width;
  691.     theRect->bottom = theRect->top + height;
  692. } // END MoveRectTo
  693.  
  694. // ---------------------------------------------------------------------------
  695.  
  696. /*
  697.     Quick 'n dirty. Mea culpa...
  698. */
  699. Boolean IsKeyDown(unsigned short theKey);
  700. Boolean IsKeyDown(unsigned short theKey) {
  701.     unsigned char km[16];
  702.  
  703.     GetKeys(*((KeyMap*) &km));
  704.     return ((km[theKey>>3] >> (theKey & 7)) & 1);
  705. }
  706.  
  707. Boolean IsCmdKeyDown() {
  708.     return IsKeyDown(0x37);
  709. }
  710.  
  711. Boolean IsControlKeyDown() {
  712.     return IsKeyDown(0x3B);
  713. }
  714.  
  715. // ---------------------------------------------------------------------------
  716.  
  717. /*
  718.     The following routines were written by Norman Basham.
  719.     They were taken from "Monitors.cpp"
  720.     
  721.     Thanks a mil, Norman!
  722. */
  723. #pragma mark === Norman Basham ===
  724.  
  725. //    ------------------------------------------------------------------------
  726. //    Given rect r, which device does it overlap most.  An example of its use
  727. //    would be passing in (**wp->visRgn).rgnBBox to find out which device a
  728. //    window is overlapping the most, as in the case of zooming a window.
  729. //    ------------------------------------------------------------------------
  730. GDHandle GetDominantDevice (Rect *r)
  731. {
  732.     GDHandle            aGDevice;
  733.     GDHandle            bigGDevice;
  734.     Rect                screenRect;
  735.     Rect                sectRect;
  736.     long                area;
  737.     long                biggestArea = 0L;
  738.  
  739.     aGDevice = GetDeviceList ();                        //    start at begining of device list
  740.     while (aGDevice != nil)                                //    loop if device exists
  741.         {
  742.         if (IsActiveScreenDevice (aGDevice))            //    if device is a monitor and active
  743.             {
  744.             screenRect = (**aGDevice).gdRect;            //    get the devices global rect
  745.             SectRect (&screenRect, r, §Rect);        //    get overlapping rect of device and r
  746.             
  747.             area = GetRectArea (sectRect);                //    changed 3/21/94
  748.             if (area > biggestArea)                        //    if overlapping rect has the biggest area
  749.                 {
  750.                 bigGDevice = aGDevice;                    //    set big device to current device
  751.                 biggestArea = area;                        //    set big area to current area
  752.                 }
  753.     
  754.             aGDevice = GetNextDevice (aGDevice);        //    check next device in list
  755.             }
  756.         }
  757.         
  758.     return bigGDevice;                                    //    return device containing biggest portion of r
  759. }
  760.  
  761. //    ------------------------------------------------------------------------
  762. //    Given a device, return wether it is a monitor and wether it's active
  763. //    ------------------------------------------------------------------------
  764. Boolean IsActiveScreenDevice(GDHandle theDevice)
  765. {
  766.     return (
  767.             (TestDeviceAttribute (theDevice, screenDevice)) &&
  768.             (TestDeviceAttribute (theDevice, screenActive))
  769.             );
  770. }
  771.  
  772. long GetRectArea(Rect r)
  773. {
  774.     Rect        temp = r;
  775.  
  776.     OffsetRect (&temp, -temp.left, -temp.top);        //    rids us of neg values
  777.     return (long) temp.right * temp.bottom;            //    return width x heigth
  778. }
  779.